using BepuUtilities;

namespace BepuPhysics
{
    /// <summary>
    /// 按以下顺序更新模拟：卧铺->集成身体姿势、速度和边界框->碰撞检测->解算器->数据结构优化。
    /// </summary>
    public class PositionFirstTimestepper : ITimestepper
    {
        /// <summary>
        /// 在休眠程序完成之后、身体集成之前激发。
        /// </summary>
        public event TimestepperStageHandler Slept;
        /// <summary>
        /// 在更新实体的位置、速度和边界框之后,但在开始碰撞检测之前。
        /// </summary>
        public event TimestepperStageHandler BeforeCollisionDetection;
        /// <summary>
        /// 在识别所有碰撞之后、但在解决约束之前激发。
        /// </summary>
        public event TimestepperStageHandler CollisionsDetected;
        /// <summary>
        /// 在求解器执行之后、数据结构增量优化之前激发。
        /// </summary>
        public event TimestepperStageHandler ConstraintsSolved;

        public void Timestep(Simulation simulation, float dt, IThreadDispatcher threadDispatcher = null)
        {
            // 请注意,将睡眠*放在*速度积分之后是有原因的。这听起来有点奇怪,但有一个很好的理由：
            // 当窄相位激活一堆物体时,它们累积的冲量将代表睡眠时作用在它们身上的所有力。
            // 这包括重力。如果我们在给定帧中应用重力之前让物体睡眠,那么当这些身体被唤醒时,累积的冲动
            // 就不那么精确了,因为他们假设重力已经被施加了。这可能会导致小的凹凸不平。
            // 因此,速度整合(和停用候选资格管理)可以在睡觉之前进行。

            // 另一方面,在开始时睡觉可以阻止使用直接唤醒时某些形式的非直觉行为。这只是个人喜好的问题。
            simulation.Sleep(threadDispatcher);
            Slept?.Invoke(dt, threadDispatcher);

            // 请注意,姿势积分器位于碰撞检测和解算之前。这与v1有所不同,在v1中首先进行碰撞检测。
            // 这是一种权衡：
            // 1)在没有解算器输入的情况下,将积分任何外部设置的速度。v1风格的外部速度控制不会工作得很好-
            // 取而代之的是,用户必须在姿势积分器运行之后改变速度。这也不是完美的,因为姿势积分器也负责
            // 用于更新用于冲突检测的边界框。
            // 2)通过将边界框计算与姿势集成捆绑在一起,避免了多余的位姿和速度内存访问。
            // 3)生成的接触位置与积分姿势同步。
            // 这对于游戏目的通常很有帮助-在创建图形效果或定位声源时,您不必重新解释联系人数据。

            // 不过,1号是个难题。没有完全"正确"的地方可以改变速度。我们可能不得不咬紧牙关创造一个
            // 惯性张量/边界框更新与姿势积分分开。如果高速缓存在其间被逐出(除非没有阶段运行,否则实际上是有保证的),
            // 这基本上意味着在带宽约为20GBps的处理器上模拟32768个物体,每帧需要额外的100-200微秒。

            // 请注意,首先使用姿势积分器而不是解算器的原因是,解算器依赖于由姿势积分计算的世界空间惯性。
            // 如果姿势积分器没有先运行,我们要么需要
            // 1)当添加对象或改变局部惯性时,复杂的世界惯性的按需更新,或者
            // 2)局部->解算器前的世界惯性计算。
            simulation.IntegrateBodiesAndUpdateBoundingBoxes(dt, threadDispatcher);
            BeforeCollisionDetection?.Invoke(dt, threadDispatcher);

            simulation.CollisionDetection(dt, threadDispatcher);
            CollisionsDetected?.Invoke(dt, threadDispatcher);

            simulation.Solve(dt, threadDispatcher);
            ConstraintsSolved?.Invoke(dt, threadDispatcher);

            simulation.IncrementallyOptimizeDataStructures(threadDispatcher);
        }
    }
}
